package sw6.girafplaceclient;

import girafPlace.lib.Application;
import girafPlace.lib.UserProfile;
import girafPlace.lib.Version;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.List;

import sw6.lib.Settings;
import sw6.lib.user.Profile;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

/**
 * Creates a connection to the server, and retrieves all the applications 
 * from the server, that fits according to the current UserProfile. 
 * A ProgressDialog is shown, while updating the list.
 */
public class NetworkTask extends AsyncTask<Void, Void, List<Application>> {
	protected String serverAddress = "home.lcdev.dk";
	protected int serverPort = 2093;
	protected String tag = "GirafPlaceClient";

	protected ProgressDialog dialog;
	protected Context context;
	protected ApplicationAdapter AA; 

	protected Socket socket;
	protected ObjectInputStream in;
	protected ObjectOutputStream out;
	
	/**
	 * Constructs a new NetworkTask in the given context.
	 * @param context The context of the application.
	 */
	public NetworkTask(Context context) {
		super();
		this.context = context;
		this.AA = ApplicationAdapter.getInstance(context);
	}
	
	@Override
	protected void onPreExecute() {	
		dialog = ProgressDialog.show(
				context,
				"Updating",
				"Please wait while the list of applications is being updated",
				true,true);

		dialog.setOnCancelListener(new OnCancelListener() {
			/**
			 * Attempts to close the socket connection. If this fails, an IOException
			 * is thrown.
			 * @param dialog The dialog window.
			 * @exception IOException If there was not a valid HTTP response from the server.
			 */
			@Override
			public void onCancel(DialogInterface dialog) {
				try {
					if(NetworkTask.this.socket != null)
						NetworkTask.this.socket.close();
				} catch (IOException e) {
					Log.e(tag, e.getMessage());
				}
				NetworkTask.this.cancel(true);
			}

		});
	}
	
	/**
	 * Tests if a connection can be established directly to the server.
	 * @param host The address of the server.
	 * @return Whether if a connection can be established. False = no, True = yes.
	 */
	public Boolean isConnected(String host){
		try {
			URL url = new URL("http://"+host);

			HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
			urlc.setRequestProperty("Connection", "close");
			urlc.setConnectTimeout(1000 * 30); // mTimeout is in seconds
			urlc.connect();
			if (urlc.getResponseCode() == 200) {
				Log.d(tag,"getResponseCode == 200");
				return new Boolean(true);
			}
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * Creates a connection to the server, and retrieves all the applications 
	 * from the server, that fits according to the current UserProfile. 
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected List<Application> doInBackground(Void... params) {
		try{
			if(!isConnected(serverAddress)){
				this.cancel(true);
				return null;
			}
			Log.d(tag,String.format("Connecting to the server on address %1$s on port %2$d",serverAddress,serverPort));
			socket = new Socket(serverAddress, serverPort);

			Log.d(tag, "Crossing the streams");
			out = new ObjectOutputStream(socket.getOutputStream());
			out.flush(); //Must be here in between, otherwise horrible bugs may appear.
			in = new ObjectInputStream(socket.getInputStream());

		} catch (Exception e) {
			Log.e(tag, e.getMessage());
			this.cancel(true);
			Toast.makeText(
					context,
					e.getMessage(),
					Toast.LENGTH_LONG).show();
		}

		if(this.isCancelled())
			return null;

		try {
			Log.d(tag,"Initiating Handshake");
			out.writeUTF("HANDSHAKE");
			out.writeUTF(Integer.toString(Version.Version));
			out.flush();

			String input = in.readUTF();

			if(input.compareTo("OLDVERSION") == 0) {
				return null;
			}
			else if (input.compareTo("WELCOME") == 0) {
				out.writeUTF("GETAPPS");
				out.flush();

				out.writeObject(buildActualProfile());

				List<Application> apps = (List<Application>) in.readObject();

				out.writeUTF("SIGNOUT");
				out.flush();
				if (in.readUTF().compareTo("KTHXBYE") == 0) {
					return apps;
				}
			} else {
				return null;
			}
		} catch (IOException e) {
			Log.e(tag, e.getMessage());
		} catch (ClassNotFoundException e) {
			Log.e(tag, e.getMessage());
		}
		return null;
	}

	@Override
	protected void onPostExecute(List<Application> result) {
		super.onPostExecute(result);

		if(result != null) {
			AA.setApps(result);
			Log.d(tag,String.format("Recieved %1$d applications from the server",result.size()));
		} else{
			Toast.makeText(context, "No connection", Toast.LENGTH_LONG).show();
			((Activity) context).finish();
		}
		dialog.dismiss();
	}

	/** Constructs a userprofile object from the lazy loading functions of sw6.lib */
	private UserProfile buildActualProfile() {
		Profile p = Settings.getUserProfile(context);
		UserProfile result = new UserProfile();

		result.setCanDragAndDrop(p.canDragAndDrop());
		result.setCanHear(p.canHear());
		result.setRequiresSimpleVisualEffects(p.requreSimpleVisualEffects());
		result.setCanAnalogTime(p.canAnalogTime());
		result.setCanDigitalTime(p.canDigitalTime());
		result.setCanRead(p.canRead());
		result.setHasBadVision(p.hasBadVision());
		result.setRequiresLargeButtons(p.requresLargeButtons());
		result.setCanSpeak(p.canSpeak());
		result.setCanNumbers(p.canNumbers());
		result.setCanUseKeyboard(p.canUseKeyboard());

		Log.d(tag,result.toString());
		return result;
	}

}